<template>
  <div ref="searchRef" class="strong-search" :class="{ active }">
    <!-- <i v-if="!prefix" class="filter-icon" @click="handleInputFocus">
      <img :src="FilterSvg" alt="" />
    </i> -->
    <el-scrollbar ref="scrollbarRef" class="search-scrollbar">
      <tag-group
        v-model="searchValue"
        @delete="handleSearch"
        @tag-click="handleActiveHide"
      />
      <el-popover
        :width="180"
        :teleported="false"
        :visible="popoverVisible"
        placement="bottom-start"
        popper-class="search-popover"
      >
        <template #reference>
          <el-input
            ref="inputRef"
            v-model="inputValue"
            class="search-input"
            :maxlength="maxLength"
            :placeholder="placeholder"
            @click="handleInputFocus"
            @blur="handleInputBlur"
            @keydown="handleInputKeyDown"
          >
            <template #prefix>
              <span v-if="prefix" class="pr-1"> {{ prefix }}: </span>
              <i class="ml-1 mr-0 filter-icon" @click="handleInputFocus">
                <img :src="FilterSvg" alt="" />
              </i>
            </template>
            <template #suffix>
              <search-close-icon
                v-if="closeBtnVisible"
                class="e-icon"
                @click.stop="handleClear"
              />
              <search-icon class="e-icon" @click.stop="handleClickSearch" />
            </template>
          </el-input>
        </template>

        <filter-list
          v-if="filterOptions.length && popoverType === 'filterList'"
          :options="filterOptions"
          @change="handleFilterChange"
        >
          <template #title>
            <slot name="filterListTitle" />
          </template>
        </filter-list>
        <search-select
          v-else-if="popoverType === 'select'"
          :options="selectOptions"
          @change="handleSelectChange"
        />
        <search-check
          v-else-if="popoverType === 'check'"
          :options="checkOptions"
          @ok="handleCheckOk"
          @cancel="handleCheckCancel"
        />
        <search-date
          v-else-if="popoverType === 'date'"
          :start-disabled-date="dateProps.startDisabledDate"
          :end-disabled-date="dateProps.endDisabledDate"
          @ok="handleDateOk"
          @cancel="handleDateCancel"
        >
          <template #title>
            <slot name="dateTitle" />
          </template>
        </search-date>
      </el-popover>
    </el-scrollbar>
  </div>
</template>

<script setup lang="ts">
import {
  ElInput,
  ElPopover,
  ElScrollbar,
  type InputInstance,
  type ScrollbarInstance
} from 'element-plus'
import {
  computed,
  type ComputedRef,
  nextTick,
  onMounted,
  provide,
  ref,
  watch,
  watchEffect
} from 'vue'

import FilterSvg from './icons/filter.svg'
import SearchCloseIcon from './icons/SearchCloseIcon.vue'
import SearchIcon from './icons/SearchIcon.vue'
import FilterList from './popover/FilterList.vue'
import SearchCheck from './popover/SearchCheck.vue'
import SearchDate from './popover/SearchDate.vue'
import SearchSelect from './popover/SearchSelect.vue'
import TagGroup from './tag/TagGroup.vue'
import type {
  FilterItem,
  LabelValue,
  PopoverOption,
  PopoverType,
  SearchValue
} from './types'

const props = withDefaults(
  defineProps<{
    modelValue?: SearchValue[]
    filterList: FilterItem[]
    placeholder?: string
    isFilter?: boolean
  }>(),
  {
    isFilter: true,
    placeholder: useI18nContent('common.global.composite_search_placeholder'),
    modelValue: () => []
  }
)

const emit = defineEmits<{
  search: [value: SearchValue[]]
  'update:modelValue': [value: SearchValue[]]
}>()

defineSlots<{
  filterListTitle(): any
  dateTitle(): any
}>()

const searchRef = ref<HTMLDivElement>()
const inputRef = ref<InputInstance>()
const scrollbarRef = ref<ScrollbarInstance>()

const active = ref(false)

const prefix = ref('')

const activeFilterItem = computed(() =>
  props.filterList.find(v => v.name === prefix.value)
)
const maxLength = computed(
  () => activeFilterItem.value?.maxLength ?? props.filterList[0]?.maxLength
)

// 默认列表第一个type
const type = computed(() => activeFilterItem.value?.type)
const popoverType = computed<PopoverType | null>(() =>
  !prefix.value ? 'filterList' : activeFilterItem.value?.popover || null
)
const inputValue = ref('')
const searchValue = ref<SearchValue[]>([...props.modelValue])

watch(
  () => props.modelValue,
  () => {
    searchValue.value = [...props.modelValue]
  },
  { deep: true }
)

// 新增的才滚动
watch(
  searchValue,
  (val, oldVal) => {
    val.length > oldVal.length && setScrollBottom()
  },
  { flush: 'post' }
)

// 搜索、过滤
const filterOptions = computed(() =>
  props.filterList.filter(v => {
    const val = inputValue.value.trim()
    if (props.isFilter) {
      return (
        !searchValue.value.some(item => item.type === v.type) &&
        (!val || v.name.includes(val))
      )
    }
    return !val || v.name.includes(val)
  })
)

const selectOptions = computed(() => {
  const val = inputValue.value.trim()
  const list = activeFilterItem.value?.popoverOption?.select || []
  return list.filter(v => !val || v.label.includes(val))
})

const checkOptions = computed(() => {
  const val = inputValue.value.trim()
  const list = activeFilterItem.value?.popoverOption?.check || []
  return list.filter(v => !val || v.label.includes(val))
})

const dateProps = computed(
  () => activeFilterItem.value?.popoverOption?.dateProps || {}
) as ComputedRef<PopoverOption['dateProps'] & {}>

const filterPlaceholder = computed(() =>
  !activeFilterItem.value?.popover
    ? `${useI18nContent('common.global.input')}${prefix.value}`
    : `${useI18nContent('common.global.select')}${prefix.value}`
)

const placeholder = computed(() =>
  prefix.value ? filterPlaceholder.value : props.placeholder
)
const closeBtnVisible = computed(
  () => !!prefix.value || !!searchValue.value.length || !!inputValue.value
)

const popoverShow = ref(false)

const popoverVisible = computed(() => {
  if (!popoverShow.value || !popoverType.value) return false
  if (popoverType.value === 'filterList' && !filterOptions.value.length) {
    return false
  }
  if (popoverType.value === 'select' && !selectOptions.value.length) {
    return false
  }
  if (popoverType.value === 'check' && !checkOptions.value.length) return false
  return true
})

function handleClear() {
  inputValue.value = ''
  prefix.value = ''
  searchValue.value = []
  handleActiveHide()
  handleSearch()
}

function handleActiveHide() {
  active.value = true
  inputRef.value?.focus()
  popoverShow.value = false
}

function popoverNextTick() {
  popoverShow.value = false
  nextTick(() => (popoverShow.value = true))
  inputRef.value?.focus()
}

function handleInputKeyDown(e: KeyboardEvent | Event) {
  const event = e as KeyboardEvent

  // 删除
  if (event.key === 'Backspace' && !inputValue.value) {
    if (prefix.value) {
      prefix.value = ''
    } else if (searchValue.value.length) {
      searchValue.value.splice(searchValue.value.length - 1, 1)
      popoverShow.value = false

      handleSearch()
    }
  }

  // 直接输入确认
  const val = inputValue.value.trim()
  if (
    val &&
    !popoverVisible.value &&
    props.filterList.length &&
    event.key === 'Enter' &&
    !activeFilterItem.value?.popover
  ) {
    autoDelFilterItem()
    setSearchValue([{ label: val, value: val }])

    prefix.value = ''
    inputValue.value = ''

    popoverShow.value = false

    handleSearch()
  }
}

function handleInputFocus() {
  active.value = true
  popoverShow.value = true
  inputRef.value?.focus()
}

const noClosePopover = ref(false)

function handleInputBlur() {
  if (noClosePopover.value) return
  active.value = false
  popoverShow.value = false
}

watchEffect(() => {
  // 点击弹窗，不主动关闭
  const noCloseTypes: PopoverType[] = ['date', 'check']
  if (
    popoverType.value &&
    noCloseTypes.includes(popoverType.value) &&
    popoverVisible.value
  ) {
    noClosePopover.value = true
  }
})

function autoDelFilterItem() {
  const firstType =
    inputValue.value.trim() && !prefix.value ? props.filterList[0]?.type : null
  const type = activeFilterItem.value?.type || firstType
  if (!props.filterList.length || !type) return

  const idx = searchValue.value.findIndex(v => v.type === type)
  idx > -1 && searchValue.value.splice(idx, 1)
}

function setSearchValue(options: LabelValue[], isDate?: boolean) {
  const firstItem = props.filterList.at(0)!
  const dateStartKey = dateProps.value.startKey
  const dateEndKey = dateProps.value.endKey
  searchValue.value.push({
    isDate,
    options,
    dateStartKey,
    dateEndKey,
    value: options.map(v => String(v.value)),
    name: prefix.value || firstItem.name,
    type: type.value || firstItem.type
  })
}

function setScrollBottom() {
  const warpRef = scrollbarRef.value?.wrapRef!
  const val = warpRef.scrollHeight - warpRef.clientHeight
  val && scrollbarRef.value?.setScrollTop(val)
}

function handleFilterChange(item: FilterItem) {
  inputValue.value = ''
  prefix.value = item.name
  handleInputFocus()
  if (popoverType.value) popoverNextTick()
  else popoverShow.value = false
}

function handleSelectChange(item: LabelValue) {
  autoDelFilterItem()
  setSearchValue([item])
  prefix.value = ''
  inputValue.value = ''
  handleActiveHide()
  handleSearch()
}

function handleCheckOk(items: LabelValue[]) {
  noClosePopover.value = false
  autoDelFilterItem()
  setSearchValue(items)
  prefix.value = ''
  inputValue.value = ''
  handleActiveHide()
  handleSearch()
}

function handleCheckCancel() {
  noClosePopover.value = false
  prefix.value = ''
  inputValue.value = ''
  handleActiveHide()
}

function handleDateOk(value: number[]) {
  noClosePopover.value = false
  autoDelFilterItem()
  const val = value.map(v => (v ? String(v) : ''))
  setSearchValue(
    val.map(v => ({ label: v, value: v })),
    true
  )
  prefix.value = ''
  inputValue.value = ''
  handleActiveHide()
  handleSearch()
}

function handleDateCancel() {
  noClosePopover.value = false
  prefix.value = ''
  inputValue.value = ''
  handleActiveHide()
}

async function handleSearch() {
  await nextTick()
  emit('update:modelValue', [...searchValue.value])
  emit('search', [...searchValue.value])
}

function handleClickSearch() {
  const val = inputValue.value.trim()
  if (val) {
    // 直接输入点击搜索
    if (
      !popoverVisible.value &&
      props.filterList.length &&
      !activeFilterItem.value?.popover
    ) {
      autoDelFilterItem()
      setSearchValue([{ label: val, value: val }])
    }
    if (prefix.value) prefix.value = ''
    inputValue.value = ''
  }

  handleSearch()
  handleActiveHide()
}

onMounted(() => {
  if (!props.filterList.length) {
    console.error('props配置 `filterList` 长度不能为 0')
  }
})

provide(
  'is-active',
  computed(() => active.value && popoverShow.value)
)
</script>

<style lang="scss">
@import url('./style.scss');
</style>
